# -*- coding: utf-8 -*-
##############################################################################
#
#    Copyright (C) 2019 RedTN
# 
#此程序是自由软件：您可以根据自由软件基金会发布的GNU Affero通用公共许可证（许可证的第3版或（由您选择）任何更高版本）的条款重新分发和/或修改它。
#这个程序是分发的，希望它是有用的，但没有任何保证；甚至没有隐含的保证适销性或适合特定用途。有关详细信息，请参阅GNU Affero通用公共许可证。
#
#您应该已经收到GNU Affero通用公共许可证的副本以及此程序。如果没有，请参见<http://www.gnu.org/licenses/>。#
###################################################################################

from odoo import fields, api, models, _
from datetime import date,datetime,timedelta,time
from ast import literal_eval
#import datetime
import math
import time as TM
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT
from dateutil.relativedelta import relativedelta
import pytz

from odoo.exceptions import UserError
import logging
_logger = logging.getLogger(__name__)

class HrOvertime(models.Model):   
    _name = "hr.overtime"
    _description = "Hr Overtime" 
    _rec_name = 'employee_id'
    _order = 'id desc'
    
    employee_id = fields.Many2one('hr.employee', string="Employee",default=lambda self: self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1))
    contract_id = fields.Many2one('hr.contract', string='Contract', compute='_compute_contract', store=True)
    # contract_id = fields.Many2one('hr.contract', string='Contract',  required=True)
    manager_id = fields.Many2one('hr.employee', string='Manager')
    start_date = fields.Datetime('Date',default = fields.Datetime.now)
    overtime_hours = fields.Float('Overtime Hours')
    notes = fields.Text(string='Notes')
    state = fields.Selection([('draft', 'Draft'), ('confirm', 'Waiting Approval'), ('refuse', 'Refused'), 
           ('validate', 'Approved'), ('cancel', 'Cancelled')], default='draft', copy=False)
    attendance_id = fields.Many2one('hr.attendance', string='Attendance')
    overtime_line_ids = fields.One2many("hr.overtime.line","overtime_id",string = "overtime lines")
    overtime_created = fields.Boolean(string = 'Overtime Created', default=False, copy=False)
    
    @api.depends('employee_id')
    def _compute_contract(self):
        # pass
        for res in self:
            contract_id = self.env['hr.contract'].sudo().search([('employee_id','=',res.employee_id.id),('state','=','open')], order='create_date desc',limit=1)
            if len(contract_id) != 1:
                raise UserError(_("The employee must have a contract!"))
            res.contract_id = contract_id
    
    def _last_day_of_month(self,any_day):
        """
        获取获得一个月中的最后一天
        :param any_day: 任意日期
        :return: string
        """
        next_month = any_day.replace(day=28) + timedelta(days=4)  # this will never fail
        return next_month - timedelta(days=next_month.day)

    def _first_day_of_month(self,any_day):
        """
        获取获得一个月中的第一天
        :param any_day: 任意日期
        :return: string
        """
        return date(any_day.year,any_day.month,1)
    

    @api.model
    def get_months_overtime(self,date_from,date_to,employee):
        """ This Function is called by payroll. 
		todo:time  local format 
        """
        ctz = pytz.timezone(self._context.get('tz') or 'UTC') 
     
        date_from = datetime.combine(self._first_day_of_month(date_from), datetime.min.time()) 
        date_to = datetime.combine(self._last_day_of_month(date_to), datetime.max.time()) 

        utc_date_from = ctz.localize(date_from).astimezone(pytz.UTC)
        utc_date_to = ctz.localize(date_to).astimezone(pytz.UTC)
        
        sql_late = '''SELECT ho.id,sum(ovt_weekday) AS ovt_weekday,sum(ovt_weekend) AS ovt_weekend,sum(ovt_legal) AS ovt_legal FROM hr_overtime_line as hol ,hr_overtime as ho 
                    WHERE ho.id = hol.overtime_id and employee_id = '%s' and hol.start_date >= '%s' and hol.end_date <= '%s' GROUP BY rollup((ho.id))'''
        self.env.cr.execute(sql_late % (employee.id,utc_date_from,utc_date_to))
        for line in self.env.cr.dictfetchall():
            if line['id']:
                #self.env['hr.overtime'].browse(line['id']).write({'overtime_created':True})
                pass
            elif (line['ovt_weekday'] or line['ovt_weekend'] or line['ovt_legal']) and not line['id']:
                employee.contract_id.write({'ovt_weekday':line['ovt_weekday'],'ovt_weekend': line['ovt_weekend'],'ovt_legal': line['ovt_legal']})
        
    
    @api.model
    def run_overtime_scheduler(self):
        """ This Function is called by scheduler. 
		todo:time  local format 
        """		
        ctz = pytz.timezone(self._context.get('tz') or self.env.user.tz or 'UTC') 
        overtime_ids = self.env['hr.overtime'].search([('overtime_created', '=', False),('state','=','validate')])
        for overtime in overtime_ids:
            overtime_line_ids = self.env['hr.overtime.line'].search([('overtime_id','=',overtime.id)])
            contract_obj = overtime.contract_id
            if not contract_obj.work_hours:
                contract_obj.work_hours = 8	
            tmp_overtime_hours = 0
            for obj in overtime_line_ids:
                #每一行初值，求和
                tp_ovt_weekday =0
                tp_ovt_weekend =0
                tp_ovt_legal =0
                for i in range((obj.end_date - obj.start_date).days+1):
                    date = obj.start_date + timedelta(days=i)
                    attend_id = self.env['hr.attendance'].search([('employee_id', '=', overtime.employee_id.id),('check_in', '>', ctz.localize(datetime.combine(date,time.min)).astimezone(pytz.UTC)),('check_in', '<',  ctz.localize(datetime.combine(date,time.max)).astimezone(pytz.UTC))],limit = 1)
                    if attend_id:
                        difference = attend_id.check_out - attend_id.check_in       
                        hour_diff = str(difference).split(':')[0]
                        min_diff = str(difference).split(':')[1]
                        tot_diff = hour_diff + '.' + min_diff
                        actual_working_hours = float(tot_diff)
                        if obj.ovt_type in 'weekend':
                            tp_ovt_weekend += math.ceil( actual_working_hours/0.5)*0.5 
                        elif obj.ovt_type in 'legal':
                            tp_ovt_legal += math.ceil( actual_working_hours/0.5)*0.5
                        else:
                            ovt_hours1 = 0
                            ovt_hours2 = 0
                            if actual_working_hours > contract_obj.work_hours:
                                tp_time1 = pytz.UTC.localize(attend_id.check_in).astimezone(ctz).time()
                                tmp_hour1 = contract_obj.start_time - (tp_time1.hour+tp_time1.minute/60.0)
                                if tmp_hour1 > 0.5:
                                    ovt_hours1 = round(tmp_hour1/0.5)*0.5
                                tp_time2 =  pytz.UTC.localize(attend_id.check_out).astimezone(ctz).time()
                                tmp_hour2 = tp_time2.hour+tp_time2.minute/60.0 - contract_obj.end_time
                                if tmp_hour2 > 0.5:
                                    if obj.supper_provided:
                                        ovt_hours2 = round(tmp_hour2/0.5)*0.5-0.5
                                    else:
                                        ovt_hours2 = round(tmp_hour2/0.5)*0.5
                                tp_ovt_weekday += (ovt_hours1 + ovt_hours2)
                obj.ovt_weekday = tp_ovt_weekday
                obj.ovt_weekend = tp_ovt_weekend
                obj.ovt_legal = tp_ovt_legal
                tmp_overtime_hours +=  tp_ovt_weekday + tp_ovt_weekend +tp_ovt_legal
            overtime.overtime_hours = tmp_overtime_hours
            overtime.overtime_created = True
    
    def action_submit(self):
        return self.write({'state':'confirm'})
        
    
    def action_cancel(self):
        return self.write({'state':'cancel'})
        
    
    def action_approve(self):
        return self.write({'state':'validate'})
    
    
    def action_refuse(self):
        return self.write({'state':'refuse'})
        
    
    def action_view_attendance(self):
        attendances = self.mapped('attendance_id')
        action = self.env.ref('hr_attendance.hr_attendance_action').read()[0]
        if len(attendances) > 1:
            action['domain'] = [('id', 'in', attendances.ids)]
        elif len(attendances) == 1:
            action['views'] = [(self.env.ref('hr_attendance.hr_attendance_view_form').id, 'form')]
            action['res_id'] = self.attendance_id.id
        else:
            action = {'type': 'ir.actions.act_window_close'}
        return action
        
class HrOvertimeLine(models.Model):
    _name = 'hr.overtime.line'
    _description = 'Hr Overtime line'

    overtime_id = fields.Many2one('hr.overtime',string='hr overtime')
    ovt_type = fields.Selection([('weekday','weekday'),('weekend','weekend'),('legal','legal_holidy')],default = 'weekday',string = 'overtime type')
    start_date = fields.Date("start Date",required = True,default=fields.Date.to_string(date.today()))
    end_date = fields.Date("end Date",required = True,default=fields.Date.to_string(date.today()))
    supper_provided = fields.Boolean(string = 'Provide Supper', default=False) 
    ovt_weekday = fields.Float(string = 'Weekday Overtime',default = 0)
    ovt_weekend = fields.Float(string = 'Weekend Overtime',default = 0)
    ovt_legal = fields.Float(string = 'Legal Holiday Overtime',default = 0)
	
    @api.constrains('start_date','end_date')
    def same_month(self):
        for res in self:
            if res.start_date.month != res.end_date.month:
                raise UserError(_("start date and end date must be the same month in a line!"))
    
    
class Contract(models.Model):
    _inherit = 'hr.contract'
    
    work_hours = fields.Float(string='Working Hours',default = 8)
    late_come = fields.Integer(string = 'Late Coming',default = 0)
    early_go = fields.Integer(string = 'Eary Going',default = 0)
    late_times=fields.Integer("late times",default=0)    
    early_times=fields.Integer("early times",default=0)    
    ovt_weekday = fields.Float(string = 'Weekday Overtime',default = 0)
    ovt_weekend = fields.Float(string = 'Weekend Overtime',default = 0)
    ovt_legal = fields.Float(string = 'Legal Holiday Overtime',default = 0)
    start_time = fields.Float("Start Time", default= 9, help='It is the time to start work for all of the company.')
    end_time = fields.Float("End Time", default= 17.5, help='It is the time to off work for all of the company.')
    overtime_salary_rate = fields.Float('overtime salary rate',default = 1.5 )
    weekend_salary_rate = fields.Float('weekend salary rate',default = 2 )
    legal_salary_rate = fields.Float('holidy salary rate',default = 3)    

       
    
class HrAttendance(models.Model):
    _inherit = "hr.attendance" 
    
    attend_created = fields.Boolean(string = 'attend Created', default=False, copy=False)
    attend_confirmed = fields.Boolean(string = 'attendance confirmed', default=False, copy=False)
    late_come=fields.Integer("late minutes",default=0, compute='_compute_late_early', store=True)    
    early_go=fields.Integer("early minutes",default=0, compute='_compute_late_early', store=True)
    is_weekday=fields.Boolean("is today weekday?", default = False,compute='_compute_weekday',store=True)

    @api.depends('check_in')
    def _compute_weekday(self):
        for res in self:
            the_day = (res.check_in + timedelta(hours=8)).weekday()
            if the_day != 5 and the_day != 6:
                res.is_weekday = True
            else:
                res.is_weekday = False
        

    @api.depends('check_in', 'check_out')
    def _compute_late_early(self):
        ctz = pytz.timezone(self._context.get('tz') or 'UTC') 
        for attendance in self:
            if  not attendance.attend_confirmed:
                contract_obj = self.env['hr.contract'].sudo().search([('employee_id', '=', attendance.employee_id.id),('state','=','open')],limit = 1)
                if attendance.check_out and contract_obj:
                    tp_time1 = pytz.UTC.localize(attendance.check_in).astimezone(ctz).time()
                    tmp_hour1 = (tp_time1.hour+tp_time1.minute/60.0)-contract_obj.start_time
                    if tmp_hour1> 0:
                        attendance.late_come = int(tmp_hour1*60+0.000001)
                    tp_time2 = pytz.UTC.localize(attendance.check_out).astimezone(ctz).time()
                    tmp_hour2 =  contract_obj.end_time -(tp_time2.hour+tp_time2.minute/60.0)
                    if tmp_hour2 > 0:
                        attendance.early_go = int(tmp_hour2*60+0.000001)
    
    @api.model
    def action_confirm(self):
        ids = self.env.context.get('active_ids')
        self.browse(ids).write({'attend_confirmed':True})

    def _last_day_of_month(self,any_day):
        """
        获取获得一个月中的最后一天
        :param any_day: 任意日期
        :return: string
        """
        next_month = any_day.replace(day=28) + timedelta(days=4)  # this will never fail
        return next_month - timedelta(days=next_month.day)

    def _first_day_of_month(self,any_day):
        """
        获取获得一个月中的第一天
        :param any_day: 任意日期
        :return: string
        """
        return date(any_day.year,any_day.month,1)
    
    @api.model
    def get_months_attend(self,start_date,end_date,employee):

        ctz = pytz.timezone(self._context.get('tz') or 'UTC') 
        

        date_from = datetime.combine(self._first_day_of_month(start_date), datetime.min.time()) 
        date_to = datetime.combine(self._last_day_of_month(end_date), datetime.max.time()) 

        utc_date_from = ctz.localize(date_from).astimezone(pytz.UTC)
        utc_date_to = ctz.localize(date_to).astimezone(pytz.UTC)

        sql_late = "SELECT id,count(id) AS times,sum(late_come) AS sum  FROM hr_attendance WHERE employee_id = '%s' and attend_confirmed = 'True' and late_come > '0' and check_in >= '%s' and check_in <= '%s' GROUP BY rollup((id))"
        self.env.cr.execute(sql_late % (employee.id,utc_date_from,utc_date_to))
        for come in self.env.cr.dictfetchall():
            if come['id']:
                #self.env['hr.attendance'].browse(come['id']).write({'attend_created':True})
                pass
            elif come['sum'] and not come['id']:
                employee.contract_id.write({'late_come':come['sum'],'late_times': come['times']})
                
        sql_early = "SELECT id,count(id) AS times,sum(early_go) AS sum FROM hr_attendance WHERE employee_id ='%s' and attend_confirmed = 'True' and  (attend_created = 'False' or attend_created is null) and early_go > '0' and check_in >= '%s' and check_in <= '%s' GROUP BY rollup((id))"
        self.env.cr.execute(sql_early % (employee.id ,utc_date_from,utc_date_to))
        for go in self.env.cr.dictfetchall():
            if go['id']:
                #self.env['hr.attendance'].browse(go['id']).write({'attend_created':True})
                pass
            elif go['sum'] and not go['id']:
                employee.contract_id.write({'early_go':go['sum'],'early_times': go['times']})
 